﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Gmantis.Controls
{
    [TemplateVisualState(Name = "Disabled", GroupName = "CommonStates"), StyleTypedProperty(Property = "ValidationDecoratorStyle", StyleTargetType = typeof(ValidationDecorator)), TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates"), TemplateVisualState(Name = "Normal", GroupName = "CommonStates"), TemplateVisualState(Name = "Valid", GroupName = "ValidationStatesStates"), TemplateVisualState(Name = "InvalidUnfocused", GroupName = "ValidationStatesStates"), TemplateVisualState(Name = "InvalidFocused", GroupName = "ValidationStatesStates")]
    public abstract class EditableContentControl : ContentControl
    {
        // Fields
        private object _actualContent;
        private bool _throwIsMouseOverChanged;
        public static readonly DependencyProperty ContentControlProperty = DependencyProperty.Register("ContentControl", typeof(ContentPresenter), typeof(EditableContentControl), new PropertyMetadata(new PropertyChangedCallback(EditableContentControl.OnContentControlPropertyChanged)));
        public static readonly DependencyProperty ContentConverterProperty = DependencyProperty.Register("ContentConverter", typeof(TypeConverter), typeof(EditableContentControl), null);
        protected static readonly DependencyProperty EditControlProperty = DependencyProperty.Register("EditControl", typeof(Control), typeof(EditableContentControl), null);
        internal static readonly DependencyProperty ForceMouseOverProperty = DependencyProperty.Register("ForceMouseOver", typeof(bool), typeof(EditableContentControl), new PropertyMetadata(new PropertyChangedCallback(EditableContentControl.OnForceMouseOverPropertyChanged)));
        internal bool IsDropDownOpen;
        public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(EditableContentControl), new PropertyMetadata(true, new PropertyChangedCallback(EditableContentControl.OnIsEditablePropertyChanged)));
        public static readonly DependencyProperty IsInEditModeProperty = DependencyProperty.Register("IsInEditMode", typeof(bool), typeof(EditableContentControl), new PropertyMetadata(false, new PropertyChangedCallback(EditableContentControl.OnIsInEditModePropertyChanged)));
        public static readonly DependencyProperty IsMouseOverProperty = DependencyProperty.Register("IsMouseOver", typeof(bool), typeof(EditableContentControl), new PropertyMetadata(new PropertyChangedCallback(EditableContentControl.OnIsMouseOverPropertyChanged)));
        internal bool SeparateActualContent;
        public static readonly DependencyProperty ValidationDecoratorStyleProperty = DependencyProperty.Register("ValidationDecoratorStyle", typeof(Style), typeof(EditableContentControl), null);

        // Events
        public event EventHandler EditCanceled;

        public event EventHandler EditCompleted;

        public event EventHandler EditStarted;

        public event EventHandler<PropertyChangedEventArgs<bool>> IsMouseOverChanged;

        // Methods
        public EditableContentControl()
        {
            RoutedEventHandler handler = null;
            DependencyPropertyChangedEventHandler handler2 = null;
            this._throwIsMouseOverChanged = true;
            base.DefaultStyleKey = typeof(EditableContentControl);
            if (handler == null)
            {
                handler = (param0, param1) => this.ChangeVisualStateCommon(false);
            }
            base.Loaded += handler;
            if (handler2 == null)
            {
                handler2 = (s, a) => this.ChangeVisualStateCommon(true);
            }
            base.IsEnabledChanged += handler2;
            base.MouseEnter += new MouseEventHandler(this.Control_MouseEnter);
            base.MouseLeave += new MouseEventHandler(this.Control_MouseLeave);
            this.SetCustomDefaultValues();
        }

        private void Control_MouseEnter(object sender, MouseEventArgs e)
        {
            if (InputEventArgs.GetPointerType(e) == PointerDeviceType.Mouse)
            {
                this.IsMouseOver = true;
            }
        }

        private void Control_MouseLeave(object sender, MouseEventArgs e)
        {
            if (InputEventArgs.GetPointerType(e) == PointerDeviceType.Mouse)
            {
                this.IsMouseOver = false;
            }
        }

        private void EditableContentControl_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.UpdateVisualState();
        }

        protected void ChangeVisualStateCommon(bool useTransitions)
        {
            if (!base.IsEnabled)
            {
                VisualStateHelper.GoToState(this, "Disabled", useTransitions);
            }
            if (base.IsEnabled)
            {
                VisualStateHelper.GoToState(this, "MouseOver", useTransitions);
            }
            if ((base.IsEnabled && !this.IsMouseOver) && !this.ForceMouseOver)
            {
                VisualStateHelper.GoToState(this, "Normal", useTransitions);
            }
        }

        private void EditControl_LostFocus(object sender, RoutedEventArgs e)
        {
            this.IsInEditMode = false;
            base.IsTabStop = true;
        }

        internal abstract void OnActualContentChanged(object old, object new_);
        public override void OnApplyTemplate()
        {
            string str = string.Empty;
            base.OnApplyTemplate();
            if (!string.IsNullOrEmpty(str))
            {
                if ((base.Template != null) && !PlatformIndependent.IsInDesignMode(this))
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Template cannot be applied to EditableContentControl.\nDetails: {0}", new object[] { str }));
                }
            }
            else
            {
                this.ChangeVisualStateCommon(false);
            }
        }

        protected sealed override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);
            if (!object.ReferenceEquals(this.ActualContent, newContent))
            {
                this.ActualContent = newContent;
            }
        }

        private void OnContentControlChanged(ContentPresenter oldValue)
        {
            if ((this.ContentControl != null) && (base.ContentTemplate != null))
            {
                this.ContentControl.ContentTemplate = base.ContentTemplate;
                Extensions.Bind(this, System.Windows.Controls.ContentControl.ContentTemplateProperty, this.ContentControl, "ContentTemplate");
            }
        }

        private static void OnContentControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EditableContentControl control = d as EditableContentControl;
            ContentPresenter oldValue = (ContentPresenter)e.OldValue;
            control.OnContentControlChanged(oldValue);
        }

        protected virtual void OnEditCanceled()
        {
            if (this.EditCanceled != null)
            {
                this.EditCanceled(this, new EventArgs());
            }
        }

        protected virtual void OnEditCompleted()
        {
            if (this.EditCompleted != null)
            {
                this.EditCompleted(this, new EventArgs());
            }
        }

        protected virtual void OnEditStarted()
        {
            if (this.EditStarted != null)
            {
                this.EditStarted(this, new EventArgs());
            }
        }

        private static void OnForceMouseOverPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as EditableContentControl).ChangeVisualStateCommon(true);
        }

        private void OnGotFocus(object sender, RoutedEventArgs e)
        {
            if ((sender == this) && this.IsEditable)
            {
                this.IsInEditMode = true;
                FrameworkElement elem = this.GetLogicalFocusedElement();
                if (((elem == null) || elem.GetAllParents().Contains(this)) || (elem == this))
                {
                    this.EditControl.LostFocus -= new RoutedEventHandler(this.EditControl_LostFocus);
                    this.EditControl.LostFocus += new RoutedEventHandler(this.EditControl_LostFocus);
                    this.EditControl.Focus();
                    base.IsTabStop = false;
                }
            }
        }

        protected virtual void OnIsEditableChanged(bool oldValue)
        {
            if (this.IsInEditMode)
            {
                this.IsInEditMode = false;
            }
        }

        private static void OnIsEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EditableContentControl control = d as EditableContentControl;
            bool oldValue = (bool)e.OldValue;
            control.OnIsEditableChanged(oldValue);
        }

        protected virtual void OnIsInEditModeChanged(bool oldValue)
        {
            if (!this.IsInEditMode)
            {
                if (this.IsDirty)
                {
                    this.OnEditCompleted();
                }
                else
                {
                    this.OnEditCanceled();
                }
            }
            else
            {
                this.OnEditStarted();
            }
            this.IsDirty = false;
            this.UpdateVisualState();
        }

        private static void OnIsInEditModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EditableContentControl control = d as EditableContentControl;
            bool oldValue = (bool)e.OldValue;
            control.OnIsInEditModeChanged(oldValue);
        }

        private static void OnIsMouseOverPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            EditableContentControl sender = d as EditableContentControl;
            if ((sender.IsMouseOverChanged != null) && sender._throwIsMouseOverChanged)
            {
                PropertyChangedEventArgs<bool> args = new PropertyChangedEventArgs<bool>
                {
                    OldValue = (bool)e.OldValue,
                    NewValue = (bool)e.NewValue
                };
                sender.IsMouseOverChanged(sender, args);
            }
            sender.ChangeVisualStateCommon(true);
        }

        private void OnKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if ((!this.IsInEditMode && this.IsEditable) && this.ShouldEnterEditModeOnKeyDown(e.Key))
            {
                this.IsInEditMode = true;
            }
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (!this.IsInEditMode && this.IsEditable)
            {
                this.IsInEditMode = true;
                this.EditControl.Focus();
                e.Handled = true;
            }
        }

        private void SetCustomDefaultValues()
        {
            base.IsEnabled = true;
            base.IsTabStop = true;
            this.UpdateVisualState();
            base.GotFocus += new RoutedEventHandler(this.OnGotFocus);
            base.MouseLeftButtonDown += new MouseButtonEventHandler(this.OnMouseLeftButtonDown);
            base.KeyDown += new KeyEventHandler(this.OnKeyDown);
            base.IsEnabledChanged += new DependencyPropertyChangedEventHandler(this.EditableContentControl_IsEnabledChanged);
        }

        protected virtual bool ShouldEnterEditModeOnKeyDown(Key key)
        {
            return true;
        }

        protected internal void UpdateVisualState()
        {
            if ((this.EditControl != null) && (this.ContentControl != null))
            {
                if (this.IsInEditMode || this.IsDropDownOpen)
                {
                    this.EditControl.Opacity = 1.0;
                    this.EditControl.IsTabStop = true;
                    this.EditControl.IsHitTestVisible = true;
                    this.ContentControl.Visibility = Visibility.Collapsed;
                    this.ContentControl.Content = null;
                }
                else
                {
                    this.EditControl.Opacity = 1.4012984643248171E-45;
                    this.EditControl.IsTabStop = false;
                    this.EditControl.IsHitTestVisible = false;
                    this.ContentControl.Visibility = Visibility.Visible;
                    this.ContentControl.Content = this.ActualContent;
                }
                base.Cursor = this.IsEditable ? Cursors.IBeam : Cursors.Arrow;
            }
        }

        // Properties
        internal object ActualContent
        {
            get
            {
                return this._actualContent;
            }
            set
            {
                if (value != this._actualContent)
                {
                    object old = this._actualContent;
                    this._actualContent = value;
                    this.OnActualContentChanged(old, this._actualContent);
                    if (!this.SeparateActualContent)
                    {
                        base.Content = this._actualContent;
                    }
                }
            }
        }

        public ContentPresenter ContentControl
        {
            get { return (ContentPresenter)GetValue(ContentControlProperty); }
            set { SetValue(ContentControlProperty, value); }
        }

        public TypeConverter ContentConverter
        {
            get { return (TypeConverter)GetValue(ContentConverterProperty); }
            set { SetValue(ContentConverterProperty, value); }
        }

        protected Control EditControl
        {
            get { return (Control)GetValue(EditControlProperty); }
            set { SetValue(EditControlProperty, value); }
        }

        internal bool ForceMouseOver
        {
            get { return (bool)GetValue(ForceMouseOverProperty); }
            set { SetValue(ForceMouseOverProperty, value); }
        }

        public virtual bool IsDirty
        {
            get { return false; }
            set { }
        }

        public bool IsEditable
        {
            get { return (bool)GetValue(IsEditableProperty); }
            set { SetValue(IsEditableProperty, value); }
        }

        public bool IsInEditMode
        {
            get { return (bool)GetValue(IsInEditModeProperty); }
            set { SetValue(IsEditableProperty, value); }
        }

        public bool IsMouseOver
        {
            get { return (bool)GetValue(IsMouseOverProperty); }
            internal set { SetValue(IsMouseOverProperty, value); }
        }

        public Style ValidationDecoratorStyle
        {
            get { return (Style)GetValue(ValidationDecoratorStyleProperty); }
            set { SetValue(ValidationDecoratorStyleProperty, value); }
        }
    }
}
